package com.plugin.uniapp.video;

import android.media.MediaCodec;
import android.media.MediaFormat;
import android.os.Build;
import android.view.Surface;

import com.plugin.uniapp.utils.Logger;

import java.nio.ByteBuffer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class H264Decoder {
    public interface NeedReconnectCallback {
        void onNeedReconnect();
    }

    public boolean isRunning = false;
    private MediaCodec mediaCodec;
    private Surface currentSurface;
    private boolean isStarted = false;
    private boolean isStoped = false;
    private MediaFormat format;
    private final BlockingQueue<byte[]> inputQueue = new LinkedBlockingQueue<>();
    private Thread decodeThread;
    private boolean hasInit = false;
    private int width, height;
    private final String deviceId;
    private NeedReconnectCallback reconnectCallback;
    private int reconnectFrameListen = 0;

    public H264Decoder(String deviceId) {
        this.deviceId = deviceId;
    }

    public void pushBytes(byte[] data) {
        if (data != null) {
            inputQueue.offer(data);
        }
    }

    public void start(Surface surface, NeedReconnectCallback callback) {
        this.currentSurface = surface;
        this.reconnectCallback = callback;
        this.reconnectFrameListen = 0;
        isRunning = true;
        isStoped = false;
        startDecodeThread();
    }

    private void startDecodeThread() {
        if (decodeThread != null && decodeThread.isAlive()) {
            return;
        }

        decodeThread = new Thread(() -> {
            while (isRunning) {
                try {
                    byte[] data = inputQueue.take();
                    if (!hasInit) {
                        boolean isSpsFrame = isSpsFrame(data);
                        if (isSpsFrame) {
                            int[] size = parseSpsForSize(data);
                            this.width = size[0];
                            this.height = size[1];
                            log("收到消息[" + data.length + "]: " + data[4] + "是SPS 解码尺寸：" + this.width + " x " + this.height);
                            initCodec();
                            hasInit = true;
                        } else {
                            log("收到消息[" + data.length + "]: " + data[4] + "不是SPS");
                            this.reconnectFrameListen++;
                            if (this.reconnectFrameListen > 100) {
                                log("连续100帧数据非SPS帧，重新连接服务器并重置解码器");
                                stop();
                                if (this.reconnectCallback != null) {
                                    log("有回调监听，已触发回调");
                                    this.reconnectCallback.onNeedReconnect();
                                    this.reconnectCallback = null;
                                } else {
                                    log("无回调监听，无法重新启动");
                                }
                            }
                        }
                    }
                    if (mediaCodec != null) {
                        int index = mediaCodec.dequeueInputBuffer(10000);
                        if (index >= 0) {
                            ByteBuffer inputBuffer = mediaCodec.getInputBuffer(index);
                            if (inputBuffer != null) {
                                inputBuffer.clear();
                                inputBuffer.put(data);
                                mediaCodec.queueInputBuffer(index, 0, data.length, System.nanoTime() / 1000, 0);
                            }
                        }
                        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                        int outIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 10000);
                        if (outIndex >= 0) {
                            mediaCodec.releaseOutputBuffer(outIndex, true); // 渲染到 Surface
                        }
                    }
                } catch (Exception e) {
                    if(!isStoped) {
                        log("init(surface)：开始重连并重新启动解码器：" + e);
                        stop();
                        if (this.reconnectCallback != null) {
                            log("init(surface)：有回调监听，已触发回调");
                            this.reconnectCallback.onNeedReconnect();
                            this.reconnectCallback = null;
                        } else {
                            log("init(surface)：无回调监听，无法重新启动");
                        }
                    }
                }
            }
        });
        decodeThread.start();
    }

    private void initCodec() {
        try {
            if (format == null) {
                format = MediaFormat.createVideoFormat("video/avc", width, height);
            }
            mediaCodec = MediaCodec.createDecoderByType("video/avc");
            mediaCodec.configure(format, this.currentSurface, null, 0); // 使用 Surface 输出
            mediaCodec.start();
            isStarted = true;
        } catch (Exception e) {
            error("init(surface)：初始化 MediaCodec 失败：" + e);
            //这里某些帧不是SPS会初始虎啊失败，但不影响后续收到SPS帧后就能成功，所以不用打印错误
        }
    }

    public void changeSurface(Surface newSurface) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            error("Android版本低于23，不支持动态切换 Surface");
            return;
        }

        if (mediaCodec == null) {
            this.currentSurface = newSurface;
            log("MediaCodec is null, 先记录 Surface 等待启动后使用");
            return;
        }

        if (newSurface == null) {
            error("新的 Surface 为空，无法切换");
            return;
        }

        if (newSurface == currentSurface) {
            error("新 Surface 和当前相同，无需切换");
            return;
        }

        try {
            if (!isStarted) {
                error("MediaCodec 还未 start，先记录 Surface，后续使用");
                this.currentSurface = newSurface;
                return;
            }

            // 尝试切换 Surface
            log("正在切换 Surface...");
            mediaCodec.setOutputSurface(newSurface);
            this.currentSurface = newSurface;
            log("切换 Surface 成功");
        } catch (IllegalStateException e) {
            error("切换 Surface 失败: " + e + " 开始尝试重建并切换");
            restartCodec(newSurface);  // 尝试恢复
        } catch (Exception e) {
            error("未知异常切换 Surface 失败: " + e + " 开始尝试重建并切换");
            restartCodec(newSurface);  // 尝试恢复
        }
    }

    private void restartCodec(Surface surface) {
        log("准备重启 MediaCodec...");
        if(isStoped)return;
        try {
            stop();
            this.currentSurface = surface;
            hasInit = false; // Reset initialization flag to wait for new SPS frame
            isRunning = true;
            startDecodeThread();
            log("MediaCodec 重建并切换 Surface 成功");
        } catch (Exception e) {
            error("MediaCodec 重建失败：" + e);
        }
    }

    public void stop() {
        isRunning = false;
        isStoped = true;
        inputQueue.clear();
        if (mediaCodec != null) {
            try {
                if (isStarted) {
                    mediaCodec.stop();
                }
                mediaCodec.release();
            } catch (Exception e) {
                error("stop()：" + e);
            } finally {
                mediaCodec = null;
                isStarted = false;
            }
        }

        if (decodeThread != null) {
            decodeThread.interrupt();
            try {
                decodeThread.join(100); // Wait for thread to finish
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            decodeThread = null;
        }

        hasInit = false;
    }

    private int[] parseSpsForSize(byte[] sps) {
        try {
            byte[] cleanedSps = NalUnitParser.cleanSps(sps);
            return NalUnitParser.parseSps(cleanedSps);
        } catch (Exception e) {
            error("SPS 提取失败: " + e.getMessage());
            return null;
        }
    }

    private boolean isSpsFrame(byte[] data) {
        return data.length > 4 && data[4] == 0x67; // 0x67 是 SPS 的 NAL type
    }

    private void log(String msg) {
        //Logger.log("[" + deviceId + "]" + msg, getClass());
    }

    private void error(String msg) {
        //Logger.error("[" + deviceId + "]" + msg, getClass());
    }
}